/*************************************************************************
 *
 *  Copyright (C) 2013 Florian Dang <florian.coin@gmail.com>
 *
 *  This file is part of NatusVerse.
 *
 *  NatusVerse is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  NatusVerse is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NatusVerse.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************/

#include "image/fractal.h"

#include <sys/time.h>

#include <iostream>
#include <cmath>
#include <algorithm>
#include <string>

#include "perfs.h"

using image::Img;

void Fractal::CallMandelbrot(int w, int h, int x, int y, float scale, float radius,
			int max_it, float color, std::string img_name)
{
	std::vector<float> img(3 * w * h);

	Img fractal(img, w, h, image::PPM);
	double time_start, time_end;

	time_start = perfs::my_gettimeofday();
	Fractal::Mandelbrot(fractal, x, y, scale, radius, max_it, color);

	time_end = perfs::my_gettimeofday();
	perfs::Display("Mandelbrot", time_end - time_start, 0.0f);

	fractal.Save(img_name);
}

void Fractal::Mandelbrot(	Img &img, int x_center, int y_center, float scale,
							float radius, int max_it, float color)
{
	int w = img.GetWidth();
	int h = img.GetHeight();
	int step = img.GetStep();

	#pragma omp parallel for schedule(dynamic)
	for (int j = 0; j < h; j ++)
	for (int i = 0; i < w; i ++)
	{
		float a = (i - x_center) * 2.0f / (w * scale);
		float b = (j - y_center) * 2.0f / (w * scale);

		float x = 0.0f;
		float y = 0.0f;

		int it = 0;

		while (x * x + y * y < radius * radius  && it < max_it )
		{
			float xtmp = x * x - y * y + a;
			y = 2 * x * y + b;
			x = xtmp;

			it ++;
		}

		if (it == max_it)
		{
			img.GetPixel(j * w * step + i * step) = 0.0f;
			img.GetPixel(j * w * step + i * step + 1) = 0.0f;
			img.GetPixel(j * w * step + i * step + 2) = 0.0f;
		}
		else
		{
			// Palette
			/*
			img.GetPixel(j * w * step + i * step) = palette_R[it % 8];
			img.GetPixel(j * w * step + i * step + 1) = palette_G[it % 8];
			img.GetPixel(j * w * step + i * step + 2) = palette_B[it % 8];
			*/

			// Smooth
			float mu = it + 1 - log(log(sqrt(x*x+y*y)))/log(2);
			float smooth = mu * color;
			smooth = ((smooth >= 768.0f) || (smooth < 0.0f)) ? (0) : (smooth);
			SetColor(img, i, j, smooth);
		}
	}
}

void Fractal::SetColor(Img &img, int i, int j, float smooth)
{
	int w = img.GetWidth();
	int step = img.GetStep();

	float r = 0.0f;
	float g = 0.0f;
	float b = 0.0f;

	if (smooth >= 512.0f)
	{
		r = smooth - 512.0f;
		g = 255.0f - r;
	}
	else if (smooth >= 256.0f)
	{
		g = smooth - 256.0f;
		b = 255.0f - g;
	}
	else
	{
		b = smooth;
	}

	img.GetPixel(j * w * step + i * step) = r;
	img.GetPixel(j * w * step + i * step + 1) = g;
	img.GetPixel(j * w * step + i * step + 2) = b;
}

void Fractal::SetColor2(Img &img, int i, int j, float smooth)
{
	int w = img.GetWidth();
	int step = img.GetStep();

	float r = 0.0f;
	float g = 0.0f;
	float b = 0.0f;

	if (smooth >= 512.0f)
	{
		r = smooth - 512.0f;
		b = 255.0f - r;
	}
	else if (smooth >= 256.0f)
	{
		b = smooth - 256.0f;
	}
	else
	{
		b = smooth;
	}

	img.GetPixel(j * w * step + i * step) = r;
	img.GetPixel(j * w * step + i * step + 1) = g;
	img.GetPixel(j * w * step + i * step + 2) = b;
}
